#
# Copyright Bryan Lawrence and James Tauber, 2004/05
# Gnu Public License
# Wiki Latex Embed Handler
#
# V0.1 January 2, 2005
# V0.2 January 6, 2005; standalone ready
#                       respect last modification time and speed up
# V0.3 January 17,2005: add code highlighting support
# V0.31 January 18, 2005: better errors
# V0.32 January 20, 2005: handling code strings better ... (avoids hangup)
# V0.4 Feburary 11, 2005: Using a scratch directory
#                         Fixing the error handling
# V0.41 Feburary 13, 2005: Support location of external binaries
# V0.42 February 22, 2005: Default scratch dir in /tmp
# V0.43 Feburary 25, 2005: remove <center> for xhtml1.1
# V0.44 March 25, 2005: Add support for access to source-highlight command line
#                       Changes default to not support numbering ...
# V0.45 March 07, 2006 Modify how latex images are handled in html terms
#					(to support proper formatting within lists)
#
import os, tempfile, popen2, glob
from xml.dom import minidom

class embedhandler:

    def __init__(self, link_prefix, embed_prefix='/__embedded__',
                 workingpath='/tmp/leoscratch',binarypath='/usr/local/bin'):
        ''' Input arguments include
                link_prefix necessary to find the location
                            of the html file and any embedded inclusions
                embed_prefix used to indicate any further additions to the URI
                            for the path to the inclusions
                workingpath is a directory location for temporary files
                binarypath is an additional directory for embeddding
                binaries like source-highlight and/or dvi2bitmap
        '''
        
        self.link_prefix = link_prefix
        self.embed_prefix = embed_prefix
        self.item=0
        #
        # check and see if binarypath is in path, if not add it
        #
        path=os.environ['PATH']
        pathdirs=path.split(':')
        if binarypath not in pathdirs: os.environ['PATH']+=':'+binarypath
        #
        self.workingpath=workingpath
        if not os.path.exists(workingpath):
            os.mkdir(workingpath)
        #
        # we care about the current directory for use when standalone ...
        self.currentdir=os.getcwd()

    def execmd(self,command):
        ''' wraps up subprocess error handling'''
        # has to be linuxacious because process handling is uncool in python
        #V0.33 and earlier:
        try:
            r=popen2.Popen3(command +' >& /dev/null ')
            res=r.wait()
            return res
        except:
            return 1

    def latex(self,payload,tmpfilebase):
        ''' Given a latex payload, and base path for tmpfiles, produce a png
        file which is the output of dvi2bitmap and then return the content of
        that file for inclusion in the "official" output file '''
          
        latex=r'''\documentclass{article}
        \usepackage{amssymb,amsmath,amscd,concmath}
        \pagestyle{empty}
        \begin{document}
        \begin{equation*}%s\end{equation*}
        \end{document}
        ''' % payload
                        
        tmplatex=tmpfilebase+'.tex'
        tmpdvi=tmpfilebase+'.dvi'
        tmppng=tmpfilebase+'.png'
            
        t=file(tmplatex,"w")
        t.write(latex)
        t.close()

        if self.execmd('latex --interaction=batchmode '+tmplatex):
            return self.preformat(payload,'Latex Error ')
        

        cmd=''.join(["dvi2bitmap ",tmpfilebase,
                     " --magnification=2 "
                     "--scale=6 --font-mode=nechi --resolution=360 ",
                     "--process=blur,crop,transparent ",
                     "--output=",tmppng])
        if self.execmd(cmd):
            return self.preformat(payload,'dvi2bitmap error ')
        try:
            f=open(tmppng,'rb')
            content=f.read()
            self.cleanup()
            return content
        except:
            return self.preformat(payload,'Unable to find '+tmppng)


    def codehighlight(self,payload,language,tmpfilebase):
        ''' takes a set of code lines, and a language definition, and uses
        the external source-highlight code operating on temporary files to
        produce some highlighted code '''

        # March 25, we expect the 'language' string to look like either
        #  python
        #  python -n (and any other source-highlight command except
        #             the -f and --src-lang ) 
        #  so the default is now no numbering


        tmpcode=tmpfilebase+'.code'
        tmpout=tmpcode+'.html'
        t=file(tmpcode,'w')
        t.write(payload)
        t.close()

        # access source-highlight args from language variable
        blank=language.find(' ')
        if blank!=-1:
            args=language[blank+1:]
            language=language[0:blank]
        else: args=''
       
        if self.execmd('source-highlight '+args+' -f xhtml --src-lang='
                       +language+' '+tmpcode+' >& /dev/null'):
            self.preformat(payload,'highlight error ')
        try:
            t=file(tmpout,'r')
            content=t.read()
            t.close()
            self.cleanup()
            return content
        except:
            return self.preformat(payload,'highlight file error')

    def embed(self,xmlstring,leofile=''):
        ''' Take an xml document consisting of a script command and a payload
        to be executed by the script (if necessary) and if necessary,
        embed the output in leofile. Outside the context of leonardo,
        leofile is not necessary '''

        os.chdir(self.workingpath)

        doc=minidom.parseString(xmlstring)
        command=doc.getElementsByTagName('script')[0].firstChild.data
        payload=doc.getElementsByTagName('payload')[0].firstChild.data
            
        # two cases, return html pointing to an image, or return html
        rimage={'late':1,'code':0}[command[0:4]]

        firstchild,parent=-1,0 # hack for  comparisons
        
        if rimage:
            self.item+=1
        
            if leofile=='':
                key=''
                leo=0
            else:
                leo=1
                self.leofile=leofile
                key=self.leofile.key.strip('/')
        
            embedded_uri=''.join([self.link_prefix,key,
                                  self.embed_prefix,str(self.item)])

            if leo:
                # we can evaluate whether we need to do anything or not
                parent=self.leofile.get_lastmod()
                firstchild=self.leofile.enclosure('embedded', self.item).get_lastmod()
                
        if firstchild<=parent:
            tmpfilebase=tempfile.mktemp()
            tmpfilebase=os.path.basename(tmpfilebase)
            self.tmpfilebase=tmpfilebase

            if leofile is None: return self.preformat(payload,' Leofile is None ')

            if command[0:5]=='latex':
                content=self.latex(payload,tmpfilebase)
                if content[0:5]=='<pre>': rimage=0
            elif command[0:4]=='code':
        
                content=self.codehighlight(payload,command[5:].rstrip(),tmpfilebase)
            else:
                return self.preformat(payload,'Unknown command '+command)

            #would prefer not to copy it ... could do directly ... but if the lfs isn't a
            #"real" file system then we probably have to do it this way ... perhaps better
            #for lfs to return a "file-type" object ... but difficult to see how
            #we could use it in external scripts ...

            if rimage:
                if leo:
                    self.leofile.enclosure('embedded',
                                           self.item).set_content(content, 'png')
                else:
                    os.chdir(self.currentdir)
                    ff=embedded_uri
                    fo=open(ff,'w')
                    fo.write(content)
                    fo.close()

        if rimage: 
            html='<img src="%s" alt="Latex Embedded Image"></img>'%embedded_uri
        else:
            html=content

        os.chdir(self.currentdir)
        return html
    
    def preformat(self,payload,message):
        self.cleanup()
        html=''.join(['<pre>',payload,'\n***\n',message,'\n***\n','</pre>'])
        return html

    def cleanup(self):
        files=glob.glob(self.tmpfilebase+'*')
        for file in files: os.remove(file)
